/*
    自定义 格式化日志信息。(可以自定义，以哪种格式，输出日志消息)
    1. Formatter中，pattern成员：保存日志输出的"格式字符串"。
        如：[%d{%H:%M%S}][%t][%p][%c][%c][%f:%l]%T%m%n
        %d 日期。会有格式化子项如：%d{%H:%:M:%S}
        %t 线程id。
        %p 日志等级。
        %c 日志器名称。
        %f 文件名。
        %l 行号。
        %T 缩进。
        %m 日志消息。
        %n 换行。
        %xyz，不存在的格式化字符，打印空。
        只有%%，才是%，单个%，无效，打印空。
        其他普通字符，直接输出。
    2. Formatter中，std::vector<FormatItem::ptr> items成员：
         用于"按序"保存格式化字符串中，格式化字符对应的对象(如：%d对应的对象，%t对应的对象)。
         格式化字符可能有子项((如：%d{%H:%M%S}，%d有子项%H:%M:%S))，
         要将这个子项传给其构造函数。
    3. 因为要将不同的格式化字符对象放在一个std::vector中，所以抽象一个基类，std::vector存放基类指针；
         再来个多态，方便vector中的元素都使用同一个函数，将其日志消息的要素，存放到消息字符串中。
*/
#ifndef __MY_LOG_FORMATTER_H__
#define __MY_LOG_FORMATTER_H__

#include "LogMessage.hpp"
#include "LogLevel.hpp"
#include <iostream>
#include <memory>
#include <vector>
#include <sstream>

namespace LzcLog
{
    class FormatChar
    {
    public:
        using ptr = std::shared_ptr<FormatChar>; // 为什么是shared_ptr？因为后面要拷贝到vector中
        virtual ~FormatChar() = default;
        virtual void Format(std::ostream &os, const LogMessage &message) = 0;
    };
    class TimeFormatChar : public FormatChar
    {
    public:
        TimeFormatChar(const std::string &sub_format = "%H:%M:%S")
        : _sub_format(sub_format)
        {
            if(_sub_format.empty()) _sub_format = "%H:%M:%S";
        }
            
        void Format(std::ostream &os, const LogMessage &message) override
        {
            time_t time = message._ctime;
            struct tm t;
            localtime_r(&time, &t);
            char s[128] = {0};
            // 子格式可能无效，strftime返回0时输出空
            if (strftime(s, sizeof(s) - 1, _sub_format.c_str(), &t) > 0)
            {
                os << s;
            }
        }

    private:
        std::string _sub_format;
    };
    class ThreadIdFormatChar : public FormatChar
    {
    public:
        void Format(std::ostream &os, const LogMessage &message) override
        {
            os << message._tid;
        }
    };
    class LogLevelFormatChar : public FormatChar
    {
    public:
        void Format(std::ostream &os, const LogMessage &message) override
        {
            os << LogLevel::LogLevelToString(message._level);
        }
    };
    class LoggerFormatChar : public FormatChar
    {
    public:
        void Format(std::ostream &os, const LogMessage &message) override
        {
            os << message._logger_name;
        }
    };
    class FileNameFormatChar : public FormatChar
    {
    public:
        void Format(std::ostream &os, const LogMessage &message) override
        {
            os << message._file_name;
        }
    };
    class LineNumFormatChar : public FormatChar
    {
    public:
        void Format(std::ostream &os, const LogMessage &message) override
        {
            os << message._line_num;
        }
    };
    class TabFormatChar : public FormatChar
    {
    public:
        void Format(std::ostream &os, const LogMessage &message) override
        {
            os << "    "; // "\t"会因为前面字符的长度，跳的距离不一样
        }
    };
    class PayloadFormatChar : public FormatChar
    {
    public:
        void Format(std::ostream &os, const LogMessage &message) override
        {
            // os << message._payload;
            os.write(message._payload.data(), message._payload.size()); // 强制按size输出
        }
    };
    class NewLineFormatChar : public FormatChar
    {
    public:
        void Format(std::ostream &os, const LogMessage &message) override
        {
            os << std::endl; // 使用'\n'不兼容
        }
    };
    class OtherFormatChar : public FormatChar
    {
    public:
        OtherFormatChar(const std::string &str = "")
            : _str(str)
        {
        }
        void Format(std::ostream &os, const LogMessage &message) override
        {
            os << _str;
        }

    private:
        std::string _str;
    };
    
    const static std::string default_pattern = "[%d{%H:%M:%S}][%t][%p][%c][%f:%l]%T%m%n";
    class LogFormatter
    {
    public:
        using ptr = std::shared_ptr<LogFormatter>;
        LogFormatter(const std::string &pattern = default_pattern)
            : _pattern(pattern)
        {
            ParsePattern(); // 重新解析默认格式
        }

        std::string format(const LogMessage &message)
        {
            std::stringstream ss;
            for (auto &format_char : _format_chars)
                format_char->Format(ss, message);

            return ss.str();
        }

    private:
        FormatChar::ptr CreateFormatChar(char key, const std::string &sub_format = "")
        {
            switch (key) {
                case 'd': return std::make_shared<TimeFormatChar>(sub_format);
                case 't': return std::make_shared<ThreadIdFormatChar>();
                case 'p': return std::make_shared<LogLevelFormatChar>();
                case 'c': return std::make_shared<LoggerFormatChar>();
                case 'f': return std::make_shared<FileNameFormatChar>();
                case 'l': return std::make_shared<LineNumFormatChar>();
                case 'T': return std::make_shared<TabFormatChar>();
                case 'm': return std::make_shared<PayloadFormatChar>();
                case 'n': return std::make_shared<NewLineFormatChar>();
                default:  return std::make_shared<OtherFormatChar>(""); // 无效key，输出空
            }
        }
        // 核心：解析格式化字符串_pattern
        void ParsePattern()
        {
// [%d{%H:%M%S}][%t][%p][%c][%f:%l]%T%m%n
/* 
    解析格式化字符串的思路。
        存储临时的字符串
            1. 收集连续的普通字符，
            2. 不为空，就插入，再清空
            3. 遍历到末尾，就退出循环
            4. pos指向%，如果%后面没有字符,认为是无效的的%,直接break
            5. 判断%后面的字符类型
                if是%
                  插入%，跳过 % + %(两个字符)
                else // 是格式化字符
                {
                  6. 判断后面是否有子项
                  if有子项，即有'{'
                      if找到对应的'}'
                        跳过'}'
                        保存sub_format
                      else // 没有对应的'}'，即没有子项
                        跳过'{'，
                  else // 没有子项
                    跳过 % + key(两个字符)

                  插入key,sub_format // sub_format为空，会使用默认格式
                 }
*/
            size_t pos = 0;
            const size_t n = _pattern.size();
            std::string tmp_str;
            while(pos < n)
            {
                // 1. 收集连续的普通字符
                while(pos < n && _pattern[pos] != '%')
                {
                    tmp_str += _pattern[pos];
                    ++pos;
                }
                // 2. tmp_str不为空，就插入，并清空
                if(!tmp_str.empty())
                {
                    _format_chars.push_back(std::make_shared<OtherFormatChar>(tmp_str));
                    tmp_str.clear();
                }
                // 3. 遍历到末尾，就退出循环
                if(pos >= n)
                    break;
                // 4. pos指向%，如果%后面没有字符，认为是无效的的%，直接break
                if(pos + 1 == n)
                    break;
                // 5. pos指向%，判断%后面的字符类型
                if(_pattern[pos+1] == '%')
                {
                    _format_chars.push_back(std::make_shared<OtherFormatChar>("%"));
                    pos += 2;
                }
                else // 是格式化字符
                {
                    // 6. 判断后面是否有子项
                    char key = _pattern[pos+1]; // 格式化字符
                    std::string sub_format;
                    size_t start = pos + 2;
                    if(start < n && _pattern[start] == '{') // 有子项，start指向'{'
                    {
                        size_t end = _pattern.find("}", start+1);
                        if(end != std::string::npos) // 找到'}'
                        {
                            pos = end + 1;
                            sub_format = _pattern.substr(start+1, end-start-1);
                        }
                        else // 没有配对的'}'，即没有子项
                        {
                            pos = start + 1; // 跳过'{'
                        }
                    }
                    else // 没有子项
                    {
                        pos += 2; // 跳过 % + key（2个字符）
                    }
                    _format_chars.push_back(CreateFormatChar(key, sub_format)); // 如果sub_format为空，会用默认的格式
                }
            }
        }
        std::string _pattern;
        std::vector<FormatChar::ptr> _format_chars;
    };
}
#endif